
#include "ChatClient.h"
#include "../utils/Logger.h"
#include "../utils/Config.h"
#include "../communication/Mensajes/MensajeIdentificatorio.h"
#include "../communication/Mensajes/MensajeTexto.h"
#include "../communication/Mensajes/MensajeQueryChats.h"
#include "../communication/Mensajes/MensajeQueryChatsResponse.h"
#include "../communication/Mensajes/MensajeAdministrarChat.h"
#include "../communication/Mensajes/MensajeAgregarUsuario.h"
#include "../communication/Mensajes/MensajeRemoverUsuario.h"
#include "../Signals/SIGINT_Handler.h"
#include "../Signals/SignalHandler.h"
#include <errno.h>
#include <iostream>
#include <getopt.h>
#include <stdio.h>
#include <sys/wait.h>
#include "../utils/Tp2Utils.h"


ChatClient::ChatClient() {}

ChatClient::~ChatClient() {}


void ChatClient::showHelp() {
	std::cout << "Comandos posibles:"
			<< " [--help|-h] "
			<< " [--nombre-usuario|-u=username] "
			<< " [--consultar-chats-existentes|-e] "
			<< " [--conectarse|-a=nombre] "
			<< " [--crear|-o=nombre]" << std::endl;

	std::cout << "\t--help -h\t\tMuestra esta ayuda" << std::endl;
	std::cout
			<< "\t--nombre-usuario -u\t\tNombre de usuario a utilizar"
			<< std::endl;
	std::cout
			<< "\t--consultar-chats-existentes -e\t\tMuestra los chats existentes para el cliente"
			<< std::endl;
	std::cout
			<< "\t--conectarse -c=nombre\tConectarse a chat con nombre 'nombre' "
			<< std::endl;
	std::cout
			<< "\t--crear -o=nombre\tCrear sala de chat y unirse a ella con nombre 'nombre'"
			<< std::endl;
}

ChatClient::Configuracion ChatClient::parseParams(int argc, char **argv) {
	Configuracion configuracion;
	struct option long_options[] = {
			{ "help", no_argument, 0, 'h' },
			{ "nombre-usuario",required_argument, 0, 'u' },
			{ "consultar-chats-existentes", required_argument, 0, 'e' },
			{ "conectarse",required_argument, 0, 'a' },
			{ "crear", required_argument, 0, 'o' },
			{ 0, 0,0, 0 }
	};
	int option_index = 0;
	int c = -1;

	while ((c = getopt_long(argc, argv, "heu:a:o:", long_options,&option_index)) != -1) {
		switch (c) {
		case 'h':
			showHelp();
			exit(0);
			break;
		case 'u':
			configuracion.nombreUsuario = optarg;
			Logger::getInstance().logMessage(Logger::TRACE, "El nombre del usuario es:");
			Logger::getInstance().logMessage(Logger::TRACE, optarg);
			break;
		case 'e':
			configuracion.listarChats= true;
			break;
		case 'a':
			configuracion.conectarse= optarg;
			break;
		case 'o':
			configuracion.crear = optarg;
			break;
		default:
			showHelp();
			exit(1);
			break;
		}
	}
	return configuracion;
}

bool ChatClient::solveClientDestination(Configuracion configuracion){

	QueueProtocoloPlano* queue = new QueueProtocoloPlano(MAIN_QUEUE,'a');
	Logger::getInstance().logMessage(Logger::TRACE, "Entro a solve destination");

	//Tengo q negociar que quiere hacer el tipo!
	bool debeSeguir = false;

	if( !configuracion.crear.empty() || !configuracion.conectarse.empty()){
		Logger::getInstance().logMessage(Logger::TRACE, "Voy a intentar conectar a un cliente");
		debeSeguir = true;
	}

	//Aca puede consultar el estado de otros chats
	if( configuracion.listarChats ){
		Logger::getInstance().logMessage(Logger::TRACE, "Chat_Client: Envio mensaje de consultar");

		MensajeQueryChats* msg = new MensajeQueryChats();
		queue->escribirMensaje(SERVER_ID,msg);
		delete msg;

		MensajeQueryChatsResponse* response = (MensajeQueryChatsResponse*)queue->leerMensaje(this->pidCliente);
		Logger::getInstance().logMessage(Logger::TRACE, "Chat_Client: Recibo respuesta del server");

		std::cout << response->toString() << std::endl;

		delete response;
	}

	//Crear nuevo chat
	if( !configuracion.crear.empty() ){
		Logger::getInstance().logMessage(Logger::TRACE, "Chat_Client: crear : " + configuracion.crear);
		MensajeAgregarUsuario* msgAgregar = new MensajeAgregarUsuario(configuracion.nombreUsuario,this->pidCliente,configuracion.conectarse);
		queue->escribirMensaje(SERVER_ID, msgAgregar);
		delete msgAgregar;
	}


	//Unirse a un chat
		//si era crear,creo chat y lo hago unirse al mismo
	if( !configuracion.conectarse.empty() ){
		Logger::getInstance().logMessage(Logger::TRACE, "Chat_Client: conectarse: " + configuracion.crear);
		MensajeAgregarUsuario* msgAgregar = new MensajeAgregarUsuario(configuracion.nombreUsuario,this->pidCliente,configuracion.conectarse);
		queue->escribirMensaje(SERVER_ID,msgAgregar);
		delete msgAgregar;
	}

	delete queue;

	return debeSeguir;
}

int ChatClient::runClient(std::string idUser){
	pid_t pid;

	switch (pid = fork())
	{
		case -1:
			Logger::getInstance().logMessage(Logger::ERROR, "Fallo el fork");
			return (-1);

			break;
		case 0:
		{
			Logger::getInstance().setProcessInformation("Client_receiver");
			//Proceso HIJO que hace el papel de receiver
			QueueProtocoloPlano* queue = new QueueProtocoloPlano(MAIN_QUEUE,'a');

			std::cout << "Ingrese su mensaje para el grupo: ";
			SIGINT_Handler sigint_handler;
			// se registra el event handler declarado antes
			SignalHandler::getInstance()->registerHandler(SIGINT, &sigint_handler);
			// este se queda en un loop enviando mensajes hasta que recibe
			// el mensaje desde consola de cerrar la conversacion

			Logger::getInstance().logMessage(Logger::TRACE, "Empiezo el envio de mensajes del cliente");
			char mensaje[MENSAJETEXTO_LIMIT];
			//Itero el envio de mensaje de texto
			while (sigint_handler.getGracefulQuit() == 0) {

				//tomo el texto de stdin y lo mando por mensaje
				std::cin.getline (mensaje,MENSAJETEXTO_LIMIT);

				Logger::getInstance().logMessage(Logger::TRACE, "Envio mensajes del cliente");
				MensajeTexto* texto = new MensajeTexto(idUser,this->pidCliente,mensaje);
				queue->escribirMensaje(SERVER_ID,texto);
				delete texto;
			}

			MensajeRemoverUsuario* msgRemover = new MensajeRemoverUsuario(idUser,this->pidCliente);
			queue->escribirMensaje(SERVER_ID,msgRemover);
			delete msgRemover;

			delete queue;

			Logger::getInstance().logMessage(Logger::TRACE, "TERMINO el envio de mensajes del cliente");


			break;
		}
		default:
		{
			Logger::getInstance().logMessage(Logger::TRACE, "Empiezo la lectura del cliente");
			MensajeConAutorYPid* mensajeRecibido = NULL;
			QueueProtocoloPlano* queue = new QueueProtocoloPlano(MAIN_QUEUE,'a');

			//Nota: el null es el EOF
			while ( (mensajeRecibido = (MensajeConAutorYPid*)queue->leerMensaje(this->pidCliente) ) != NULL){

				switch (mensajeRecibido->getType()) {
					case REMOVER_USUARIO:{
						std::cout << "El usuario se desconecto! " << "|" << mensajeRecibido->getUserId()  << std::endl;
					}break;
					case AGREGAR_USUARIO:{
						std::cout << "El usuario se conecto! " << "|" << mensajeRecibido->getUserId()  << std::endl;
					}break;
					case MENSAJE_TEXTO:{
						MensajeTexto* texto = (MensajeTexto*) mensajeRecibido;
						std::cout << mensajeRecibido->getUserId() << "|" << texto->getMensaje() << std::endl;
					}break;
					default:
						Logger::getInstance().logMessage(Logger::ERROR, "Chat client:Recibi un mensaje que no se manejar. Tipo : " + UtilString::intToString(mensajeRecibido->getType()) );
						break;
				}

				delete mensajeRecibido;
			}

			delete queue;

			Logger::getInstance().logMessage(Logger::TRACE, "TERMINO la lectura del cliente");

			break;
		}
	}

	return 0;
}

int ChatClient::runProcess(int argc, char **argv)
{
	showHelp();
    Configuracion configuracion = parseParams(argc, argv);

	Logger::getInstance().setProcessInformation("Client_sender");


	std::string idUser =configuracion.nombreUsuario;

	Logger::getInstance().logMessage(Logger::TRACE, "main - nombre Usuario a usar: " + configuracion.nombreUsuario);

	this->pidCliente = getpid();
	 //Resuelvo lo que el cliente quiere hacer
	if ( solveClientDestination(configuracion) ){
		Logger::getInstance().logMessage(Logger::TRACE, "main - Corro usuario: " + configuracion.nombreUsuario);
		return runClient(idUser);
	}

	return 0;
}



